Skip to content

feat(io): implement CacheIO read-cache layer for disk IO#2419

Open
LHT129 wants to merge 1 commit into
antgroup:mainfrom
LHT129:feature/opencode-cacheio-read-cache
Open

feat(io): implement CacheIO read-cache layer for disk IO#2419
LHT129 wants to merge 1 commit into
antgroup:mainfrom
LHT129:feature/opencode-cacheio-read-cache

Conversation

@LHT129

@LHT129 LHT129 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add CacheIO template class that provides a page-based read cache in front of disk-backed IO implementations. All reads go through the cache; writes passthrough directly to the inner IO with no cache involvement.

Closes #2420

Design

  • Architecture: CacheIO : BasicIO<CacheIO> - follows the existing CRTP IO pattern
  • Page-based caching: fixed 128KB page size via Page::DEFAULT_PAGE_SIZE
  • Total cache size: configurable with default 256MB
  • Eviction: PageCache abstraction with LRUPageCache as the first implementation
  • Read path: all reads go through cache; miss loads page from InnerIO
  • Write path: direct passthrough to InnerIO; this layer assumes read/write ranges do not overlap
  • DirectRead: always copies to a caller-owned buffer and sets need_release=true to avoid exposing evictable cache storage
  • Thread safety: mutex-protected page cache with LRU eviction

Components

  • src/io/cache_io/page.h - RAII cache page with fixed 128KB page size
  • src/io/cache_io/page_cache.h/.cpp - PageCache storage abstraction
  • src/io/cache_io/lru_page_cache.h/.cpp - LRU PageCache implementation
  • src/io/cache_io/cache_io_parameter.h/.cpp - CacheIOParameter (total_cache_size, eviction_strategy, inner_io_type)
  • src/io/cache_io/cache_io.h - CacheIO template class
  • src/io/cache_io/*_test.cpp - Unit tests for CacheIO and page cache components
  • examples/cpp/406_feature_cache_io.cpp - HGraph CacheIO example

Testing

  • Target object compilation for CacheIO source/tests passed on the remote dev environment
  • GitHub CI validates full build and tests

@LHT129
LHT129 requested a review from wxyucs as a code owner July 6, 2026 07:40
Copilot AI review requested due to automatic review settings July 6, 2026 07:40
@LHT129
LHT129 requested a review from inabao as a code owner July 6, 2026 07:40
@LHT129 LHT129 added the kind/improvement Optimizations, UX polish, or minor improvements 性能优化、体验打磨或细节改良 label Jul 6, 2026
@LHT129
LHT129 requested a review from jiaweizone as a code owner July 6, 2026 07:40
@LHT129 LHT129 added version/0.18 1. Major pyramid upgrade 2. HGraph 3. Observability 1. pyramid大升级 2. HGraph增强 3. 可观测性增强 kind/improvement Optimizations, UX polish, or minor improvements 性能优化、体验打磨或细节改良 labels Jul 6, 2026
@vsag-bot

vsag-bot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

/label S-waiting-on-review
/waiting-on reviewer
/request-review @jiaweizone
/request-review @wxyucs
/request-review @inabao

@mergify

mergify Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 All 3 merge protections satisfied — ready to merge.

Show 3 satisfied protections

🟢 Require kind label

  • label~=^kind/

🟢 Require version label

  • label~=^version/

🟢 Require linked issue for feature/bug PRs

  • body~=(?im)(?:^|[\s\-\*])(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s*:?\s+(?:#\d+|[\w.\-]+/[\w.\-]+#\d+|https?://github\.com/[\w.\-]+/[\w.\-]+/issues/\d+)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces CacheIO, a page-based read-cache IO layer wrapping an underlying disk-backed IO implementation, along with its configuration parameters, an LRU eviction strategy, and unit tests. The review feedback highlights several critical issues that must be addressed: potential Use-After-Free (UAF) vulnerabilities in ReadImpl and DirectReadImpl due to race conditions and lack of page pinning, uninitialized and un-updated this->size_ values that will cause read operations to fail, a potential division-by-zero crash if page_size is configured as zero, and a redundant if-else block during eviction strategy initialization.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io_parameter.cpp Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
@LHT129 LHT129 self-assigned this Jul 6, 2026
@LHT129 LHT129 added version/1.0 and removed version/0.18 1. Major pyramid upgrade 2. HGraph 3. Observability 1. pyramid大升级 2. HGraph增强 3. 可观测性增强 labels Jul 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new CacheIO<InnerIO> CRTP IO wrapper that adds a page-based in-memory read cache in front of an underlying IO implementation, plus JSON-parseable parameters and unit tests.

Changes:

  • Added CacheIO<InnerIO> template with page caching, LRU eviction, and DirectRead/MultiRead support.
  • Added CacheIOParameter (JSON serialization/parsing) and EvictionStrategy/LRUEviction.
  • Wired cache_io into IO parameter parsing and header aggregation; added unit tests.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/io/io_headers.h Exposes cache_io.h through the IO header umbrella.
src/io/common/io_parameter.cpp Adds JSON parsing support for type=cache_io parameters.
src/io/CMakeLists.txt Builds cache_io_parameter.cpp as part of the IO object library.
src/io/cache_io/eviction_strategy.h Introduces eviction strategy interface and LRU implementation.
src/io/cache_io/cache_io.h Implements the CacheIO read-cache IO wrapper.
src/io/cache_io/cache_io_test.cpp Adds unit tests for caching, eviction, direct read, multiread, and parameters.
src/io/cache_io/cache_io_parameter.h Declares CacheIOParameter (page size, total cache size, eviction strategy).
src/io/cache_io/cache_io_parameter.cpp Implements JSON (de)serialization for CacheIOParameter.
src/inner_string_params.h Registers the cache_io IO type string.

Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io_parameter.cpp
Comment thread src/io/common/io_parameter.cpp
Comment thread src/io/cache_io/cache_io.h Outdated
@LHT129 LHT129 added kind/feature Brand-new functionality or capabilities 引入全新的功能、新特性或新能力 and removed kind/improvement Optimizations, UX polish, or minor improvements 性能优化、体验打磨或细节改良 labels Jul 6, 2026
@LHT129
LHT129 force-pushed the feature/opencode-cacheio-read-cache branch from ec12104 to d4e7d6b Compare July 6, 2026 08:33
Copilot AI review requested due to automatic review settings July 6, 2026 08:33
@LHT129
LHT129 force-pushed the feature/opencode-cacheio-read-cache branch from d4e7d6b to 0b45a24 Compare July 6, 2026 08:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
@LHT129
LHT129 force-pushed the feature/opencode-cacheio-read-cache branch from 0b45a24 to 0c604cd Compare July 6, 2026 09:36
Copilot AI review requested due to automatic review settings July 6, 2026 11:32
@LHT129
LHT129 force-pushed the feature/opencode-cacheio-read-cache branch from 0c604cd to 33f1b3a Compare July 6, 2026 11:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread src/io/cache_io/cache_io_parameter.cpp
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

src/io/cache_io/page.h:62

  • Page::SetPageSize mutates a process-wide page size after pages may already have been allocated. Since Page instances don’t store their own allocation size, changing page_size_ can make later reads/copies compute offsets using a new size against buffers allocated with an old size, leading to out-of-bounds memory access/corruption (e.g., multiple CacheIO instances with different page sizes, or tests changing page size between runs). Consider making page size immutable (set-once) or storing the page size per Page/CacheIO instance instead of a mutable static.
    static void
    SetPageSize(uint64_t page_size) {
        static std::mutex mtx;
        std::lock_guard<std::mutex> lock(mtx);
        page_size_ = page_size;
    }

src/io/cache_io/cache_io.h:76

  • WriteImpl updates the inner IO but doesn’t invalidate cached pages that overlap the written range. If the same region was previously read and cached, subsequent reads can return stale data. Unless the entire library guarantees non-overlapping read/write regions, it’s safer to evict affected pages on write to preserve read-after-write coherence.
    void
    WriteImpl(const uint8_t* data, uint64_t size, uint64_t offset) {
        inner_io_->WriteImpl(data, size, offset);
        if (offset + size > this->size_) {
            this->size_ = offset + size;
        }
    }

src/io/cache_io/cache_io.h:117

  • The PR description states DirectRead should return an internal cache pointer for a single-page hit (need_release=false), but this implementation always allocates/copies and sets need_release=true. Also, returning a raw pointer into a cached page would require a pin/unpin mechanism to keep the Page alive until Release(); otherwise eviction could free the page while the caller still uses the pointer. Either update the PR description to match the safe allocate/copy behavior, or introduce a pinned-handle design that makes returning internal pointers safe.
    [[nodiscard]] const uint8_t*
    DirectReadImpl(uint64_t size, uint64_t offset, bool& need_release) const {
        need_release = false;
        if (size == 0 or not is_valid_range(size, offset)) {
            return nullptr;
        }
        auto* data = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(size));
        if (data == nullptr) {
            return nullptr;
        }
        if (not ReadImpl(size, offset, data)) {
            this->allocator_->Deallocate(data);
            return nullptr;
        }
        need_release = true;
        return data;
    }

src/io/cache_io/cache_io.h:55

  • CacheIOParameter exposes eviction_strategy_, but CacheIO currently always constructs LRUPageCache and never consults the parameter. This makes the configuration knob misleading and doesn’t match the PR description’s “pluggable eviction strategy” design. Consider either wiring strategy selection in CacheIO (even if only lru is supported for now) or removing/deferring the parameter until multiple strategies exist.
    CacheIO(const CacheIOParameter& param, Allocator* allocator, Args&&... args)
        : BasicIO<CacheIO<IOTmpl>>(allocator),
          cache_(std::make_unique<LRUPageCache>(std::max(
              uint64_t(1), param.total_cache_size_ / std::max(uint64_t(1), param.page_size_)))),
          inner_io_(std::make_unique<IOTmpl>(std::forward<Args>(args)...)) {
        Page::SetPageSize(std::max(uint64_t(1), param.page_size_));
        this->size_ = inner_io_->size_;

src/io/common/io_parameter.cpp:66

  • IOParameter::GetIOParameterByJson now parses type: cache_io, but other runtime IO-type validation/selection paths do not recognize it (e.g., src/factory/factory.cpp:is_valid_streaming_io_type only allows memory/block_memory/buffer/async/mmap/reader). This can lead to configuration that parses successfully but is later rejected or cannot be instantiated. Please ensure all IO factories/validators that consume IOParameter are updated to support cache_io, or avoid advertising it as a JSON-selectable type until it can be constructed end-to-end.
        } else if (type_name == IO_TYPE_VALUE_MMAP_IO) {
            io_ptr = std::make_shared<MMapIOParameter>();
            io_ptr->FromJson(json);
        } else if (type_name == IO_TYPE_VALUE_CACHE_IO) {
            io_ptr = std::make_shared<CacheIOParameter>();
            io_ptr->FromJson(json);
        } else if (type_name == IO_TYPE_VALUE_READER_IO) {

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1051 changed lines across 15 files).
Submitted 5 inline comments.

Reviewed commit df9d769.

Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/common/io_parameter.cpp
Comment thread src/io/cache_io/cache_io.h Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io_parameter.h Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

src/io/cache_io/page.h:67

  • Page uses a process-wide static page_size_ that is mutated by CacheIO constructors. Creating multiple CacheIO instances with different page_size values (or tests that call Page::SetPageSize) can make existing cached Page buffers and page-id calculations inconsistent, risking out-of-bounds reads/copies.
    static void
    SetPageSize(uint64_t page_size) {
        static std::mutex mtx;
        std::lock_guard<std::mutex> lock(mtx);
        page_size_ = page_size;
    }

    static uint64_t
    PageSize() {
        return page_size_;
    }

src/io/cache_io/cache_io.h:76

  • WriteImpl updates the inner IO but never invalidates cached pages. If a caller reads data into the cache, then overwrites the same region via Write, subsequent reads can return stale cached bytes. Either enforce the “no overlapping reads/writes” precondition at runtime or invalidate the affected pages on write.
    void
    WriteImpl(const uint8_t* data, uint64_t size, uint64_t offset) {
        inner_io_->WriteImpl(data, size, offset);
        if (offset + size > this->size_) {
            this->size_ = offset + size;
        }
    }

src/io/cache_io/cache_io.h:117

  • DirectReadImpl always allocates/copies and sets need_release=true. This contradicts the PR description that single-page direct reads should return an internal cache pointer with need_release=false. If the intended contract is “always allocate”, the PR description/tests should be updated; if the intended contract is “return cache pointer”, the implementation needs a safe pinning/lifetime strategy to prevent eviction from invalidating the returned pointer.
    [[nodiscard]] const uint8_t*
    DirectReadImpl(uint64_t size, uint64_t offset, bool& need_release) const {
        need_release = false;
        if (size == 0 or not is_valid_range(size, offset)) {
            return nullptr;
        }
        auto* data = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(size));
        if (data == nullptr) {
            return nullptr;
        }
        if (not ReadImpl(size, offset, data)) {
            this->allocator_->Deallocate(data);
            return nullptr;
        }
        need_release = true;
        return data;
    }

Comment thread src/io/cache_io/cache_io.h

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 10 comments.

Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/datacell/flatten_interface.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp Outdated
Comment thread src/io/cache_io/cache_io_test.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 10 comments.

Comments suppressed due to low confidence (1)

src/algorithm/ivf/ivf.cpp:49

  • The default IVF JSON template injects CACHE_IO_* fields into IO param blocks even when {TYPE_KEY} is not cache_io (e.g., memory_io, block_memory_io). This makes the template misleading and can cause unrelated IO parameter parsers to receive cache-specific keys. Prefer only including these fields when the IO type is actually cache_io (or nest them under an explicit inner_io/cache_io object) to keep the template self-consistent.
static constexpr const char* IVF_PARAMS_TEMPLATE =

Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/datacell/graph_interface.cpp Outdated
Comment thread src/algorithm/ivf/ivf.cpp Outdated
Comment thread src/algorithm/ivf/ivf.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 9 comments.

Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page.h Outdated
Comment thread src/io/cache_io/page_cache_test.cpp Outdated
Comment thread examples/cpp/406_feature_cache_io.cpp
Comment thread src/io/cache_io/cache_io.h Outdated

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1629 changed lines across 25 files).
Submitted 9 inline comments.

Reviewed commit b292577.

Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/algorithm/ivf/ivf.cpp Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/datacell/flatten_interface.cpp
Comment thread src/datacell/bucket_interface.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (4)

src/io/cache_io/page.h:35

  • Page uses a process-wide static page_size_ that is mutated by Page::SetPageSize(). Since Page allocates allocator_->Allocate(page_size_) at construction time, changing the global page size later (e.g., by constructing another CacheIO with a different page_size_) can make existing cached pages too small/large for subsequent reads, risking out-of-bounds reads/writes and memory corruption.
 * All pages share a process-wide uniform page size.
 */
class Page {
public:
    explicit Page(Allocator* allocator) : allocator_(allocator) {
        data_ = static_cast<uint8_t*>(allocator_->Allocate(page_size_));
    }

src/io/cache_io/cache_io.h:100

  • WriteImpl does not invalidate cached pages that overlap the written range. If a page was previously read into the cache, a later write to the same region can leave the cache serving stale data on subsequent reads.
    void
    WriteImpl(const uint8_t* data, uint64_t size, uint64_t offset) {
        inner_io_->WriteImpl(data, size, offset);
        if (offset + size > this->size_) {
            this->size_ = offset + size;
        }
    }

src/io/cache_io/cache_io.h:141

  • DirectReadImpl always allocates + copies (sets need_release=true) and never returns a pointer into the cache on single-page hits. This contradicts the PR description’s stated behavior for DirectRead, and it also means DirectRead has no zero-copy fast path.
    [[nodiscard]] const uint8_t*
    DirectReadImpl(uint64_t size, uint64_t offset, bool& need_release) const {
        need_release = false;
        if (size == 0 or not is_valid_range(size, offset)) {
            return nullptr;
        }
        auto* data = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(size));
        if (data == nullptr) {
            return nullptr;
        }
        if (not ReadImpl(size, offset, data)) {
            this->allocator_->Deallocate(data);
            return nullptr;
        }
        need_release = true;
        return data;
    }

src/io/cache_io/cache_io.h:62

  • CacheIOParameter exposes eviction_strategy_, but CacheIO always constructs LRUPageCache and ignores the strategy setting. If only LRU is supported right now, it would be clearer to either remove the parameter or explicitly document/validate that only "lru" is accepted.
    CacheIO(const CacheIOParameter& param, Allocator* allocator, Args&&... args)
        : BasicIO<CacheIO<IOTmpl>>(allocator),
          cache_(std::make_unique<LRUPageCache>(std::max(
              uint64_t(1), param.total_cache_size_ / std::max(uint64_t(1), param.page_size_)))),
          inner_io_(std::make_unique<IOTmpl>(std::forward<Args>(args)...)) {
        Page::SetPageSize(std::max(uint64_t(1), param.page_size_));
        this->size_ = inner_io_->size_;
    }

Comment thread src/io/cache_io/cache_io_test.cpp Outdated
Comment thread src/io/cache_io/cache_io_test.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 17 comments.

Comments suppressed due to low confidence (4)

src/io/cache_io/page_cache.cpp:50

  • PageCache::Insert can spin forever if PickVictim() returns a page_id that isn't present in pages_ (pages_.size() never decreases). It also calls OnRemove() even when nothing was erased, which can desync the eviction state.
    while (pages_.size() >= max_pages_) {
        uint64_t victim = PickVictim();
        if (victim == UINT64_MAX) {
            return page;
        }
        pages_.erase(victim);
        OnRemove(victim);
    }

src/io/cache_io/cache_io.h:114

  • ReadImpl uses Options::Instance().cache_page_size() without guarding against 0, which would cause division/modulo by zero when computing page_id/page_offset.
        }
        uint64_t page_size = Options::Instance().cache_page_size();
        uint64_t cur_size = 0;

src/io/cache_io/cache_io.h:192

  • get_or_load_page() uses Options::Instance().cache_page_size() without guarding against 0, which can lead to division-by-zero behavior when calculating file_offset.

        uint64_t page_size = Options::Instance().cache_page_size();
        uint64_t file_offset = page_id * page_size;
        if (file_offset >= this->size_) {

src/io/cache_io/cache_io.h:147

  • DirectReadImpl currently always allocates + copies into a new buffer and sets need_release=true. This contradicts the PR description/design notes that single-page hits should return an internal cache pointer with need_release=false. Either implement a safe page-pinning mechanism (so the cached page stays alive while the caller uses the pointer) or update the design/docs to match the current always-copy behavior.
    [[nodiscard]] const uint8_t*
    DirectReadImpl(uint64_t size, uint64_t offset, bool& need_release) const {
        need_release = false;
        if (size == 0 or not is_valid_range(size, offset)) {
            return nullptr;
        }
        auto* data = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(size));
        if (data == nullptr) {
            return nullptr;
        }
        if (not ReadImpl(size, offset, data)) {
            this->allocator_->Deallocate(data);
            return nullptr;
        }
        need_release = true;
        return data;
    }

Comment thread src/datacell/graph_interface.cpp Outdated
Comment thread src/datacell/flatten_interface.cpp
Comment thread src/datacell/bucket_interface.cpp
Comment thread src/algorithm/ivf/ivf.cpp
Comment thread src/algorithm/ivf/ivf.cpp
Comment thread src/io/cache_io/page_cache_test.cpp Outdated
Comment thread src/io/cache_io/lru_page_cache_test.cpp
Comment thread src/io/cache_io/lru_page_cache_test.cpp Outdated
Comment thread src/io/cache_io/lru_page_cache_test.cpp Outdated
Comment thread src/io/cache_io/lru_page_cache_test.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (8)

src/datacell/graph_interface.cpp:90

  • CacheIOParameter doesn’t define inner_io_parameter_, so this branch won’t compile. Use the parsed string field (inner_io_type_) instead when selecting the CacheIO specialization.
                if (cache_param) {
                    auto inner_type = cache_param->inner_io_type_;

src/datacell/flatten_interface.cpp:310

  • CacheIOParameter doesn’t have inner_io_parameter_, so this code won’t compile. Use inner_io_type_ (set from JSON) to choose the CacheIO instantiation.
        auto cache_param = std::dynamic_pointer_cast<CacheIOParameter>(param->io_parameter);
        if (cache_param) {
            auto inner_type = cache_param->inner_io_type_;

src/datacell/bucket_interface.cpp:120

  • CacheIOParameter doesn’t define inner_io_parameter_, so this code won’t compile. Use inner_io_type_ to determine which CacheIO specialization to build.
    if (io_type_name == IO_TYPE_VALUE_CACHE_IO) {
        auto cache_param = std::dynamic_pointer_cast<CacheIOParameter>(param->io_parameter);
        if (cache_param) {
            auto inner_type = cache_param->inner_io_type_;
            if (inner_type == IO_TYPE_VALUE_MMAP_IO) {

src/io/cache_io/cache_io.h:95

  • CacheIO(io_param, common_param) doesn’t validate inner_io_type_. If it’s missing/invalid, GetIOParameterByJson can return nullptr and the inner IO construction will fail later without a clear error. Validate inner_io_type_ and the returned inner_io_param and throw a helpful exception early.
        JsonType inner_json = cache_param->original_json_;
        inner_json[TYPE_KEY].SetString(cache_param->inner_io_type_);
        auto inner_io_param = IOParameter::GetIOParameterByJson(inner_json);
        inner_io_ = std::make_unique<IOTmpl>(inner_io_param, common_param);
        this->size_ = inner_io_->size_;

src/io/cache_io/cache_io.h:78

  • CacheIO uses Options::Instance().cache_page_size() as mutable global state (including setting it in the ctor) and also relies on it for page indexing and copies. If multiple CacheIO instances are created with different page sizes, or cache_page_size is changed after pages are allocated, reads can compute offsets/copy sizes with a different page size than Page allocated, risking out-of-bounds access.
                           page_size > 0 ? page_size : Options::Instance().cache_page_size())))),
          inner_io_(std::make_unique<IOTmpl>(std::forward<Args>(args)...)) {
        if (page_size > 0) {
            Options::Instance().set_cache_page_size(page_size);
        }

src/io/cache_io/cache_io.h:135

  • PR description says DirectRead should return an internal cache pointer on a single-page hit with need_release=false, but DirectReadImpl always allocates/copies and sets need_release=true. Either update the implementation to match the stated design (including safe lifetime/pinning semantics) or adjust the PR description/tests to reflect the actual behavior.
    [[nodiscard]] const uint8_t*
    DirectReadImpl(uint64_t size, uint64_t offset, bool& need_release) const {
        need_release = false;
        if (size == 0 or not is_valid_range(size, offset)) {
            return nullptr;

src/algorithm/hgraph/hgraph_param_mapping.cpp:199

  • HGraph exposes cache_page_size parameters (and constants.h declares HGRAPH__CACHE_PAGE_SIZE), and examples/tests set base_cache_page_size/precise_cache_page_size, but map_hgraph_param() doesn’t map any of those external keys into IO_PARAMS_KEY/CACHE_IO_PAGE_SIZE_KEY. As a result, user-provided page sizes are silently ignored and only the template default (131072) applies.
            HGRAPH_BASE_CACHE_TOTAL_SIZE,
            {
                BASE_CODES_KEY,
                IO_PARAMS_KEY,
                CACHE_IO_TOTAL_CACHE_SIZE_KEY,

src/algorithm/ivf/ivf.cpp:143

  • IVF exposes base_cache_page_size/precise_cache_page_size (constants.h declares IVF_*_CACHE_PAGE_SIZE), but CheckAndMappingExternalParam only maps cache total size + inner io type. External page size overrides are never mapped into CACHE_IO_PAGE_SIZE_KEY, so user-provided page sizes are ignored.
            IVF_BASE_CACHE_TOTAL_SIZE,
            {
                BUCKET_PARAMS_KEY,
                IO_PARAMS_KEY,
                CACHE_IO_TOTAL_CACHE_SIZE_KEY,

Comment thread src/io/cache_io/page.h
Comment thread src/constants.cpp
Comment thread src/constants.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.

Comment thread src/io/cache_io/page_cache_test.cpp Outdated
Comment thread include/vsag/constants.h
Comment thread include/vsag/constants.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io_parameter.cpp Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp
Comment thread src/io/cache_io/cache_io_parameter.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 8 comments.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io_parameter.h
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/io/cache_io/cache_io_parameter.cpp
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread examples/cpp/406_feature_cache_io.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/algorithm/ivf/ivf.cpp:1

  • The default IO params template includes CACHE_IO_TOTAL_CACHE_SIZE_KEY while the IO {TYPE_KEY} is not cache_io (here it’s memory_io). If IO parameter parsing is strict (or becomes strict later), these extra keys can become a compatibility hazard, and even if parsing is permissive it makes the templates harder to reason about (keys that don’t apply to the selected type). Consider only emitting cache-specific keys when {TYPE_KEY} is cache_io, or nesting them under a cache-io-specific object so the meaning is unambiguous.

Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/io/cache_io/cache_io.h
Comment thread src/datacell/flatten_interface.cpp

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1479 changed lines across 25 files).
Submitted 6 inline comments.

Reviewed commit ef98da8.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread include/vsag/constants.h
Comment thread src/io/cache_io/cache_io_test.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/algorithm/hgraph/hgraph_param_mapping.cpp:1

  • The HGraph params template adds CACHE_IO_TOTAL_CACHE_SIZE_KEY under IO params even when the IO type is {IO_TYPE_VALUE_BLOCK_MEMORY_IO}. Besides the same “unknown key” concern as IVF, this snippet also appears to hardcode a slightly different value (268435256) compared to the 256MB default used elsewhere (268435456), which is likely an unintended typo and results in a non-256MB default cache size if/when the key is honored.
// Copyright 2024-present the vsag project

Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/algorithm/ivf/ivf.cpp Outdated
Comment thread src/io/cache_io/cache_io_test.cpp
Comment thread examples/cpp/406_feature_cache_io.cpp
Comment thread src/datacell/flatten_interface.cpp

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1474 changed lines across 25 files).
Submitted 7 inline comments.

Reviewed commit 7264845.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/datacell/bucket_interface.cpp
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread include/vsag/constants.h
Comment thread src/io/cache_io/cache_io_test.cpp Outdated
Comment thread src/io/cache_io/cache_io_test.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

src/algorithm/hgraph/hgraph_param_mapping.cpp:1

  • This template injects {CACHE_IO_TOTAL_CACHE_SIZE_KEY} even though the IO {TYPE_KEY} is {IO_TYPE_VALUE_BLOCK_MEMORY_IO}. Also, the literal value here (268435256) differs from the 256MB default used elsewhere (268435456) and looks like an accidental typo (off by 200 bytes). Recommend removing the cache-only key for non-cache IO types, and if it should remain, correcting the size to 268435456 for consistency.
// Copyright 2024-present the vsag project

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/algorithm/ivf/ivf.cpp
Comment thread src/datacell/flatten_interface.cpp
Comment thread src/io/cache_io/cache_io_parameter.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

src/algorithm/hgraph/hgraph_param_mapping.cpp:1

  • The template injects {CACHE_IO_TOTAL_CACHE_SIZE_KEY} into IO params even when {TYPE_KEY} is block_memory_io. Also, the value here appears to be 268435256 (off by 100) while other defaults use 268435456. If these keys are intended only for cache_io, they should not appear in non-cache IO templates; and the default size should be consistent (likely 268435456).
// Copyright 2024-present the vsag project

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/datacell/bucket_datacell.h
Comment thread src/io/cache_io/cache_io_parameter.cpp
Comment thread examples/cpp/406_feature_cache_io.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 6 comments.

Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread src/datacell/bucket_interface.cpp
Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp
Comment thread tests/test_ivf.cpp
Comment thread examples/cpp/406_feature_cache_io.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 6 comments.

Comment thread src/io/cache_io/page_cache.cpp
Comment thread src/datacell/bucket_datacell.h
Comment thread src/algorithm/hgraph/hgraph_param_mapping.cpp
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h
Comment thread examples/cpp/406_feature_cache_io.cpp Outdated

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1783 changed lines across 30 files).
Submitted 5 inline comments.

Reviewed commit 6303999.

Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/datacell/bucket_datacell.h Outdated
Comment thread src/io/cache_io/cache_io.h
Comment thread src/io/cache_io/cache_io.h

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Comment thread src/io/cache_io/page_cache.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (2)

src/datacell/flatten_interface.cpp:333

  • This exception message drops the actual inner_io_type value, making configuration errors hard to diagnose. Include the value (or explicitly handle the empty-string case) in the error message.
            throw VsagException(ErrorType::INVALID_ARGUMENT, "Unsupported CacheIO inner_io_type");

src/datacell/bucket_interface.cpp:133

  • This exception message drops the actual inner_io_type value, making configuration errors hard to diagnose. Include the value (or explicitly handle the empty-string case) in the error message.
            throw VsagException(ErrorType::INVALID_ARGUMENT, "Unsupported CacheIO inner_io_type");

Comment thread examples/cpp/406_feature_cache_io.cpp Outdated
Comment thread examples/cpp/407_feature_ivf_cache_io.cpp Outdated
Comment thread src/datacell/graph_interface.cpp Outdated
Comment thread src/io/cache_io/page_cache.cpp

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1800 changed lines across 30 files).
Submitted 6 inline comments.

Reviewed commit eed155d.

Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/datacell/bucket_datacell.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h Outdated
Comment thread src/io/cache_io/cache_io.h
Signed-off-by: LHT129 <tianlan.lht@antgroup.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/io/cache_io/cache_io.h:52

  • CacheIO declares static constexpr bool InMemory = true, but several call sites use IOTmpl::InMemory to decide whether to treat an IO as disk-backed (e.g. src/datacell/flatten_datacell.h:326 uses if constexpr (not IOTmpl::InMemory) to select the MultiRead/IO-stats path). With InMemory=true, CacheIO<AsyncIO/BufferIO/MMapIO> will be treated as in-memory and skip that disk path, which can disable batching and IO stats in queries even though the backend is still disk-backed.
    static constexpr bool InMemory = true;
    static constexpr bool UseNonContinuous = not IOTmpl::InMemory;
    static constexpr bool SkipDeserialize = IOTmpl::SkipDeserialize;

src/datacell/bucket_datacell.h:43

  • AdjustBucketCacheIOParam uses a ceil division to compute pages_per_bucket, which can make the total cache capacity across all buckets exceed the user-configured total_cache_size_ by up to (bucket_count - 1) * Page::DEFAULT_PAGE_SIZE (potentially very large when bucket_count is large). This can violate memory limits unexpectedly.
    uint64_t total_pages = cache_param->total_cache_size_ / Page::DEFAULT_PAGE_SIZE;
    uint64_t pages_per_bucket = (total_pages + bucket_count - 1) / bucket_count;
    adjusted_param->total_cache_size_ = pages_per_bucket * Page::DEFAULT_PAGE_SIZE;

@vsag-bot vsag-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review completed.

Review effort: high (1822 changed lines across 30 files).
Submitted 3 inline comments.

Reviewed commit d9527e2.

}
auto adjusted_param = std::make_shared<CacheIOParameter>(*cache_param);
uint64_t total_pages = cache_param->total_cache_size_ / Page::DEFAULT_PAGE_SIZE;
uint64_t pages_per_bucket = (total_pages + bucket_count - 1) / bucket_count;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep the per-bucket split within the global cache budget

Ceiling this quotient assigns at least one page to every bucket whenever any budget exists, so the total becomes ceil(total_pages / bucket_count) * bucket_count. With a one-page budget and the supported 100×100 GNO-IMI layout, touching all buckets can retain 10,000 pages (about 1.22 GiB) instead of 128 KiB; the default 2,048-page budget similarly expands to 10,000 pages. Distribute a fixed number of whole pages while allowing some buckets zero pages, or share one cache across buckets.

template <typename IOTmpl>
class CacheIO : public BasicIO<CacheIO<IOTmpl>> {
public:
static constexpr bool InMemory = true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep disk-backed CacheIO out of in-memory execution paths

This is true even for AsyncIO, BufferIO, and MMapIO backends. FlattenDataCell::query therefore skips its disk MultiRead path and allocates/copies once per candidate through DirectReadImpl, while HGraph::EstimateMemory counts the entire graph/code capacity rather than the bounded cache. Preserve the inner IO's residency classification and use a separate trait or accounting path for resident cache pages.


bool
ReadImpl(uint64_t size, uint64_t offset, uint8_t* data) const {
if (not is_valid_range(size, offset)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Synchronize size validation with concurrent writes

is_valid_range reads inherited size_ before acquiring io_mutex_, while WriteImpl, ResizeImpl, and ShrinkImpl modify it under that mutex. HGraph supports concurrent add/search, so appending codes while searches read existing codes creates an unsynchronized C++ data race; DirectReadImpl also performs this check. Validate using a synchronized size snapshot or under the IO lock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in-review kind/feature Brand-new functionality or capabilities 引入全新的功能、新特性或新能力 module/api module/example module/testing size/XXL version/1.1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add CacheIO read-cache layer for disk IO

4 participants